// C:\GDBForWindows\GDBForWindows.bat
// gcc -shared -static -O2 yme.c cimgui.dll glfw3.dll -o yme.dll

#include <stdint.h>  // For uint64_t
#include <string.h>  // For memset
#include <math.h>    // For pow
#include <Windows.h> // For GetFileAttributes

#if 0
// C:\GDBForWindows\GDBForWindows.bat
// gcc -static -O2 main.c yme.dll -o print_yme_header.exe
// ./print_yme_header.exe > yme.h

#include <stdio.h>

const char * ymeGetHeader();

int main() {
  const char * header = ymeGetHeader();
  printf("%s\n", header);
}
#endif // #if 0
#define u32 unsigned
#define u64 uint64_t
#define f32 float
#define f64 double
#define YME_DECLSPEC __declspec(dllexport)
#include "yme.h"
#undef u32
#undef u64
#undef f32
#undef f64

// ymePtsdkLightGetType

typedef enum YmeLightType {
  YME_LIGHT_TYPE_NONE        = 0,
  YME_LIGHT_TYPE_DIRECTIONAL = 1,
  YME_LIGHT_TYPE_SPOT        = 2,
  YME_LIGHT_TYPE_POINT       = 3,
} YmeLightType;

// ymeAppDonutLogCallback

typedef enum YmeDonutLogSeverity {
  YME_DONUT_LOG_SEVERITY_NONE    = 0,
  YME_DONUT_LOG_SEVERITY_DEBUG   = 1,
  YME_DONUT_LOG_SEVERITY_INFO    = 2,
  YME_DONUT_LOG_SEVERITY_WARNING = 3,
  YME_DONUT_LOG_SEVERITY_ERROR   = 4,
  YME_DONUT_LOG_SEVERITY_FATAL   = 5,
} YmeDonutLogSeverity;

#define GLFW_INCLUDE_NONE
#include "glfw3.h"
#define CIMGUI_DEFINE_ENUMS_AND_STRUCTS
#include "cimgui.h"

// NOTE(Constantine):
// Add these to the beginning and the end of the yme.hstring copy of yme.h:
// R"(
// )"
#if 1
const char * g_yme_hstring = {
#include "yme.hstring"
};
#else
const char * g_yme_hstring = "Email at: iamvfx@gmail.com";
#endif

YmeFunctionPointers g_ymeFuncPtrs          = {0};
YmeData             g_ymeData              = {0};
int                 g_windowWidth          = 1280;
int                 g_windowHeight         = 720;
int                 g_monitorRefreshRate   = 60;
float               g_mouseSensitivity     = 0.025;
double              g_prevMouseX           = 0;
double              g_prevMouseY           = 0;
int                 g_cameraMode           = 0;
float               g_cam1PosX             = 0;
float               g_cam1PosY             = 0;
float               g_cam1PosZ             = 0;
float               g_cam1PitchDeg         = 0;
float               g_cam1YawDeg           = 0;
float               g_cameraMoveSpeed      = 1;
int                 g_enableAnimationsLoop = 0;
float               g_sceneTimeIncrement   = 0.001;
int64_t             g_sceneTimeIncrementDelayCounter      = 0;
int                 g_sceneTimeIncrementDelayFramesCount  = 1;
int                 g_resetAccumulationOnCameraMove       = 1;
int                 g_resetAccumulationOnTimeIncrement    = 1;
int                 g_saveScreenshotOnTimeIncrement       = 0;
int                 g_copyXformsToCameraFromInstance      = 0;
int                 g_copyXformsToCameraFromInstanceIndex = 0;
char                g_sceneJsonFilepathToLoad[16387]      = {};

const char * ymeGetHeader() {
  return g_yme_hstring;
}

void ymeAppDonutLogCallback(int severity, const char * severityText, const char * message) {
  volatile int x = 0;
}

void ymeAppInitResolutionOverrideCallback(unsigned * deviceParams_backBufferWidth, unsigned * deviceParams_backBufferHeight, void * deviceParams_startFullscreen__bool) {
  char * deviceParams_startFullscreen = (char *)deviceParams_startFullscreen__bool;
  
  deviceParams_backBufferWidth[0]  = g_windowWidth;
  deviceParams_backBufferHeight[0] = g_windowHeight;
}

void ymeAppInitCallback(const YmeFunctionPointers * ptsdkFunctionPointers, const YmeData * ptsdkData) {
  g_ymeFuncPtrs = ptsdkFunctionPointers[0];
  g_ymeData     = ptsdkData[0];

  glfwSetWindowTitle((GLFWwindow *)g_ymeData.ptrTo_m_Window__GLFWwindow, "Illuminati GLTF Ray Tracer (procedural.itch.io/illuminati)");
  const GLFWvidmode * vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
  g_monitorRefreshRate = vidmode->refreshRate;

  char * pShowUi      = (char *)g_ymeData.ptrTo_m_ui_ShowUI__bool;
  char * pEnableVsync = (char *)g_ymeData.ptrTo_m_ui_EnableVsync__bool;
  int  * pFpsLimiter  = (int *)g_ymeData.ptrTo_m_ui_FPSLimiter__int;

  pShowUi[0]      = 1;
  pEnableVsync[0] = 0;
  pFpsLimiter[0]  = g_monitorRefreshRate;
}

void ymeAppSceneLoadedOverrideInvisibleLightsCleanupCallback(int * doCleanup) {
  doCleanup[0] = 0;
}

void ymeAppDearImguiInitFontSizeOverrideCallback(float * fontSize) {
  fontSize[0] = 21.f;
}

const char * g_lastPickMaterialName     = 0;
const char * g_lastPickMeshName         = 0;
const char * g_lastPickMeshInstanceName = 0;

static void CameraUpdate(
  GLFWgamepadstate gamepadState,
  float * camPosX,
  float * camPosY,
  float * camPosZ,
  float * camPitchDeg,
  float * camYawDeg
)
{
  float framerateSensitivity = 60.f / g_monitorRefreshRate;

  if (0) { // Gamepad
    camPitchDeg[0] += 1.25f * ( (gamepadState.axes[3] > 0.135f || gamepadState.axes[3] < -0.135f) ? gamepadState.axes[3] : 0.f ) * framerateSensitivity;
    camYawDeg[0]   -= 1.25f * ( (gamepadState.axes[2] > 0.135f || gamepadState.axes[2] < -0.135f) ? gamepadState.axes[2] : 0.f ) * framerateSensitivity;
  } else { // Mouse
    camPitchDeg[0] += 1.25f * ( gamepadState.axes[3] ) * framerateSensitivity;
    camYawDeg[0]   -= 1.25f * ( gamepadState.axes[2] ) * framerateSensitivity;
  }

  float pitchRad = camPitchDeg[0] * 3.141592653589793238f / 180.f;
  float yawRad   = camYawDeg[0]   * 3.141592653589793238f / 180.f;
  float orbitX   = 0;
  float orbitY   = 0;
  float orbitZ   = 0;
  float orbitW   = 0;
  g_ymeFuncPtrs.ymePtsdkDonutMathRotationQuatFromEulerRad(pitchRad, yawRad, 0, &orbitX, &orbitY, &orbitZ, &orbitW);
  float targetRotation_row0_x = 0.f;
  float targetRotation_row0_y = 0.f;
  float targetRotation_row0_z = 0.f;
  float targetRotation_row1_x = 0.f;
  float targetRotation_row1_y = 0.f;
  float targetRotation_row1_z = 0.f;
  float targetRotation_row2_x = 0.f;
  float targetRotation_row2_y = 0.f;
  float targetRotation_row2_z = 0.f;
  g_ymeFuncPtrs.ymePtsdkDonutMathQuatToFloat3x3(orbitX, orbitY, orbitZ, orbitW,
    &targetRotation_row0_x,
    &targetRotation_row0_y,
    &targetRotation_row0_z,
    &targetRotation_row1_x,
    &targetRotation_row1_y,
    &targetRotation_row1_z,
    &targetRotation_row2_x,
    &targetRotation_row2_y,
    &targetRotation_row2_z
  );

  float cameraRightX = -targetRotation_row0_x;
  float cameraRightY = -targetRotation_row0_y;
  float cameraRightZ = -targetRotation_row0_z;

  float cameraUpX    =  targetRotation_row1_x;
  float cameraUpY    =  targetRotation_row1_y;
  float cameraUpZ    =  targetRotation_row1_z;

  float cameraDirX   =  targetRotation_row2_x;
  float cameraDirY   =  targetRotation_row2_y;
  float cameraDirZ   =  targetRotation_row2_z;

  camPosX[0] += (gamepadState.buttons[0] == 1 ? 0.0015f : 0.035f) * cameraRightX * ( (gamepadState.axes[0] > 0.135f || gamepadState.axes[0] < -0.135f) ? gamepadState.axes[0] : 0.f ) * g_cameraMoveSpeed * framerateSensitivity;
  camPosY[0] += (gamepadState.buttons[0] == 1 ? 0.0015f : 0.035f) * cameraRightY * ( (gamepadState.axes[0] > 0.135f || gamepadState.axes[0] < -0.135f) ? gamepadState.axes[0] : 0.f ) * g_cameraMoveSpeed * framerateSensitivity;
  camPosZ[0] += (gamepadState.buttons[0] == 1 ? 0.0015f : 0.035f) * cameraRightZ * ( (gamepadState.axes[0] > 0.135f || gamepadState.axes[0] < -0.135f) ? gamepadState.axes[0] : 0.f ) * g_cameraMoveSpeed * framerateSensitivity;

  camPosX[0] -= (gamepadState.buttons[0] == 1 ? 0.0015f : 0.035f) * cameraDirX   * ( (gamepadState.axes[1] > 0.135f || gamepadState.axes[1] < -0.135f) ? gamepadState.axes[1] : 0.f ) * g_cameraMoveSpeed * framerateSensitivity;
  camPosY[0] -= (gamepadState.buttons[0] == 1 ? 0.0015f : 0.035f) * cameraDirY   * ( (gamepadState.axes[1] > 0.135f || gamepadState.axes[1] < -0.135f) ? gamepadState.axes[1] : 0.f ) * g_cameraMoveSpeed * framerateSensitivity;
  camPosZ[0] -= (gamepadState.buttons[0] == 1 ? 0.0015f : 0.035f) * cameraDirZ   * ( (gamepadState.axes[1] > 0.135f || gamepadState.axes[1] < -0.135f) ? gamepadState.axes[1] : 0.f ) * g_cameraMoveSpeed * framerateSensitivity;

  camPosX[0] -= (gamepadState.buttons[0] == 1 ? 0.0005f : 0.015f) * cameraUpX    * ( (gamepadState.axes[4] > 0.135f || gamepadState.axes[4] < -0.135f) ? gamepadState.axes[4] : 0.f ) * g_cameraMoveSpeed * framerateSensitivity;
  camPosY[0] -= (gamepadState.buttons[0] == 1 ? 0.0005f : 0.015f) * cameraUpY    * ( (gamepadState.axes[4] > 0.135f || gamepadState.axes[4] < -0.135f) ? gamepadState.axes[4] : 0.f ) * g_cameraMoveSpeed * framerateSensitivity;
  camPosZ[0] -= (gamepadState.buttons[0] == 1 ? 0.0005f : 0.015f) * cameraUpZ    * ( (gamepadState.axes[4] > 0.135f || gamepadState.axes[4] < -0.135f) ? gamepadState.axes[4] : 0.f ) * g_cameraMoveSpeed * framerateSensitivity;

  camPosX[0] += (gamepadState.buttons[0] == 1 ? 0.0005f : 0.015f) * cameraUpX    * ( (gamepadState.axes[5] > 0.135f || gamepadState.axes[5] < -0.135f) ? gamepadState.axes[5] : 0.f ) * g_cameraMoveSpeed * framerateSensitivity;
  camPosY[0] += (gamepadState.buttons[0] == 1 ? 0.0005f : 0.015f) * cameraUpY    * ( (gamepadState.axes[5] > 0.135f || gamepadState.axes[5] < -0.135f) ? gamepadState.axes[5] : 0.f ) * g_cameraMoveSpeed * framerateSensitivity;
  camPosZ[0] += (gamepadState.buttons[0] == 1 ? 0.0005f : 0.015f) * cameraUpZ    * ( (gamepadState.axes[5] > 0.135f || gamepadState.axes[5] < -0.135f) ? gamepadState.axes[5] : 0.f ) * g_cameraMoveSpeed * framerateSensitivity;

  g_ymeFuncPtrs.ymePtsdkCameraUpdate(g_ymeData.ptrTo_m_Camera__donut_app_FirstPersonCamera,
    camPosX[0], camPosY[0], camPosZ[0],
    cameraDirX, cameraDirY, cameraDirZ,
    cameraUpX, cameraUpY, cameraUpZ,
    cameraRightX, cameraRightY, cameraRightZ
  );

  if (g_resetAccumulationOnCameraMove == 1) {
    char  * pResetAccumulation = (char *)g_ymeData.ptrTo_m_ui_ResetAccumulation__bool;
    static float g_prevCamPosX = 0;
    static float g_prevCamPosY = 0;
    static float g_prevCamPosZ = 0;
    static float g_prevCamDirX = 0;
    static float g_prevCamDirY = 0;
    static float g_prevCamDirZ = 0;
    if (
      g_prevCamPosX != camPosX[0] ||
      g_prevCamPosY != camPosY[0] ||
      g_prevCamPosZ != camPosZ[0] ||
      g_prevCamDirX != cameraDirX ||
      g_prevCamDirY != cameraDirY ||
      g_prevCamDirZ != cameraDirZ
    )
    {
      pResetAccumulation[0] = 1;
    }
    g_prevCamPosX = camPosX[0];
    g_prevCamPosY = camPosY[0];
    g_prevCamPosZ = camPosZ[0];
    g_prevCamDirX = cameraDirX;
    g_prevCamDirY = cameraDirY;
    g_prevCamDirZ = cameraDirZ;
  }
}

static bool FileExists(const char * filepath) {
  DWORD fileAttributes = GetFileAttributes(filepath);
  return (fileAttributes != INVALID_FILE_ATTRIBUTES && !(fileAttributes & FILE_ATTRIBUTE_DIRECTORY));
}

void ymeAppUiCallback(float * m_currentScale) {
  if (((char *)g_ymeData.ptrTo_m_ui_ShowUI__bool)[0] == 0) {
    return;
  }

  static int g_initOnce = 0;
  if (g_initOnce == 0) {
    g_initOnce = 1;

    int  * pFpsLimiter = (int *)g_ymeData.ptrTo_m_ui_FPSLimiter__int;
    pFpsLimiter[0] = g_monitorRefreshRate;

    ImGuiStyle * style = igGetStyle();
    // Gamma correction
    for (int i = 0; i < ImGuiCol_COUNT; i += 1) {
      style->Colors[i].x = pow(style->Colors[i].x, 2);
      style->Colors[i].y = pow(style->Colors[i].y, 2);
      style->Colors[i].z = pow(style->Colors[i].z, 2);
    }
  }

  igBegin("Control Panel", 0, ImGuiWindowFlags_HorizontalScrollbar);

  static int g_imguiShowDemoWindow = 0;
  struct ImVec2 defaultButtonSize = {0};
  if (igButton("Show demo window", defaultButtonSize)) {
    g_imguiShowDemoWindow = !g_imguiShowDemoWindow;
  }
  if (g_imguiShowDemoWindow == 1) {
    igShowDemoWindow(0);
  }
  igSameLine(0, 10);
  igText("Note: F2 key press toggles UI visibility.");

  static int showThanksToText = 0;
  if (igButton(" Special thanks ", defaultButtonSize)) {
    showThanksToText = !showThanksToText;
  }
  if (showThanksToText == true) {
    igSameLine(0, 10);
    igText("Special thanks to Irina Salnikova, Alexander Tarasenkov, Vitaliy Tarasenkov, Mario Rodriguez Palomino, Viacheslav Silchenko");
  }

  {
    igText("");
    igInputText("Filepath to .scene.json", g_sceneJsonFilepathToLoad, sizeof(g_sceneJsonFilepathToLoad) - 1, 0, 0, 0);
    if (igButton("Load .scene.json", defaultButtonSize) && FileExists(g_sceneJsonFilepathToLoad)) {
      g_ymeFuncPtrs.ymePtsdkSetCurrentSceneName(g_ymeData.ptrTo_m_app__App, g_sceneJsonFilepathToLoad);
      g_ymeFuncPtrs.ymePtsdkAppBaseSetAsynchronousLoadingEnabled(g_ymeData.ptrTo_m_app__App, 0);
      g_ymeFuncPtrs.ymePtsdkAppBaseBeginLoadingScene(g_ymeData.ptrTo_m_app__App, g_sceneJsonFilepathToLoad);
    }
    igText("Note: O key press loads .scene.json as well.");
    igText("");
    static float g_floatSlidersDragPrecision = 0.001;
    igInputFloat("Float sliders drag precision", &g_floatSlidersDragPrecision, 0, 0, 0, 0);
    igText("Note: Double click on a slider to enter a specific value.");
    igText("");
    igText("Note: Middle Mouse Button or Ctrl + R keys toggle between UI and WASD fly camera modes.");
    igDragFloat("Camera pos X", &g_cam1PosX, g_floatSlidersDragPrecision, 0, 0, 0, 0);
    igDragFloat("Camera pos Y", &g_cam1PosY, g_floatSlidersDragPrecision, 0, 0, 0, 0);
    igDragFloat("Camera pos Z", &g_cam1PosZ, g_floatSlidersDragPrecision, 0, 0, 0, 0);
    igDragFloat("Camera rot pitch deg", &g_cam1PitchDeg, g_floatSlidersDragPrecision, 0, 0, 0, 0);
    igDragFloat("Camera rot yaw deg", &g_cam1YawDeg, g_floatSlidersDragPrecision, 0, 0, 0, 0);
    igDragFloat("Camera look sensitivity", &g_mouseSensitivity, g_floatSlidersDragPrecision, 0, 0, 0, 0);
    igDragFloat("Camera move speed", &g_cameraMoveSpeed, g_floatSlidersDragPrecision, 0, 0, 0, 0);
    igText("");
    igText("Note: meshes are picked with the mouse cursor and Right Mouse Button.");
    igText("Last picked material      name: %s", g_lastPickMaterialName     != 0 ? g_lastPickMaterialName     : "");
    igText("Last picked mesh          name: %s", g_lastPickMeshName         != 0 ? g_lastPickMeshName         : "");
    igText("Last picked mesh instance name: %s", g_lastPickMeshInstanceName != 0 ? g_lastPickMeshInstanceName : "");
    static float g_lastMeshInstancePosX   = 0;
    static float g_lastMeshInstancePosY   = 0;
    static float g_lastMeshInstancePosZ   = 0;
    static float g_lastMeshInstanceQuatX  = 0;
    static float g_lastMeshInstanceQuatY  = 0;
    static float g_lastMeshInstanceQuatZ  = 0;
    static float g_lastMeshInstanceQuatW  = 0;
    static float g_lastMeshInstanceScaleX = 0;
    static float g_lastMeshInstanceScaleY = 0;
    static float g_lastMeshInstanceScaleZ = 0;
    if (((int *)g_ymeData.ptrTo_m_ui_SelectedMaterialID__int)[0] != 0) {
      int instanceIndex = ((int *)g_ymeData.ptrTo_m_ui_SelectedInstanceIndex__int)[0];
      void * m_Scene = 0;
      g_ymeFuncPtrs.ymePtsdkGetCurrentScenePointer(g_ymeData.ptrTo_m_app__App, &m_Scene);
      void * meshInstance = 0;
      g_ymeFuncPtrs.ymePtsdkGetSceneGraphMeshInstance(m_Scene, instanceIndex, &meshInstance);
      void * meshInstanceNode = 0;
      g_ymeFuncPtrs.ymePtsdkSceneGraphLeafGetNode(meshInstance, &meshInstanceNode);
      double px = 0;
      double py = 0;
      double pz = 0;
      double qx = 0;
      double qy = 0;
      double qz = 0;
      double qw = 0;
      double sx = 0;
      double sy = 0;
      double sz = 0;
      g_ymeFuncPtrs.ymePtsdkSceneGraphNodeGetTranslation(meshInstanceNode, &px, &py, &pz);
      g_ymeFuncPtrs.ymePtsdkSceneGraphNodeGetRotation(meshInstanceNode, &qx, &qy, &qz, &qw);
      g_ymeFuncPtrs.ymePtsdkSceneGraphNodeGetScaling(meshInstanceNode, &sx, &sy, &sz);
      g_lastMeshInstancePosX   = px;
      g_lastMeshInstancePosY   = py;
      g_lastMeshInstancePosZ   = pz;
      g_lastMeshInstanceQuatX  = qx;
      g_lastMeshInstanceQuatY  = qy;
      g_lastMeshInstanceQuatZ  = qz;
      g_lastMeshInstanceQuatW  = qw;
      g_lastMeshInstanceScaleX = sx;
      g_lastMeshInstanceScaleY = sy;
      g_lastMeshInstanceScaleZ = sz;
    }
    igText("Last picked mesh instance position:");
    bool _0 = igDragFloat("Instance pos X", &g_lastMeshInstancePosX, g_floatSlidersDragPrecision, 0, 0, 0, 0);
    bool _1 = igDragFloat("Instance pos Y", &g_lastMeshInstancePosY, g_floatSlidersDragPrecision, 0, 0, 0, 0);
    bool _2 = igDragFloat("Instance pos Z", &g_lastMeshInstancePosZ, g_floatSlidersDragPrecision, 0, 0, 0, 0);
    igText("Last picked mesh instance quaternion:");
    bool _3 = igDragFloat("Instance quat X", &g_lastMeshInstanceQuatX, g_floatSlidersDragPrecision, 0, 0, 0, 0);
    bool _4 = igDragFloat("Instance quat Y", &g_lastMeshInstanceQuatY, g_floatSlidersDragPrecision, 0, 0, 0, 0);
    bool _5 = igDragFloat("Instance quat Z", &g_lastMeshInstanceQuatZ, g_floatSlidersDragPrecision, 0, 0, 0, 0);
    bool _6 = igDragFloat("Instance quat W", &g_lastMeshInstanceQuatW, g_floatSlidersDragPrecision, 0, 0, 0, 0);
    igText("Last picked mesh instance scale:");
    bool _7 = igDragFloat("Instance scale X", &g_lastMeshInstanceScaleX, g_floatSlidersDragPrecision, 0, 0, 0, 0);
    bool _8 = igDragFloat("Instance scale Y", &g_lastMeshInstanceScaleY, g_floatSlidersDragPrecision, 0, 0, 0, 0);
    bool _9 = igDragFloat("Instance scale Z", &g_lastMeshInstanceScaleZ, g_floatSlidersDragPrecision, 0, 0, 0, 0);
    if (_0 || _1 || _2 || _3 || _4 || _5 || _6 || _7 || _8 || _9) {
      int instanceIndex = ((int *)g_ymeData.ptrTo_m_ui_SelectedInstanceIndex__int)[0];
      void * m_Scene = 0;
      g_ymeFuncPtrs.ymePtsdkGetCurrentScenePointer(g_ymeData.ptrTo_m_app__App, &m_Scene);
      void * meshInstance = 0;
      g_ymeFuncPtrs.ymePtsdkGetSceneGraphMeshInstance(m_Scene, instanceIndex, &meshInstance);
      void * meshInstanceNode = 0;
      g_ymeFuncPtrs.ymePtsdkSceneGraphLeafGetNode(meshInstance, &meshInstanceNode);
      g_ymeFuncPtrs.ymePtsdkSceneGraphNodeSetScaling(meshInstanceNode, g_lastMeshInstanceScaleX, g_lastMeshInstanceScaleY, g_lastMeshInstanceScaleZ);
      g_ymeFuncPtrs.ymePtsdkSceneGraphNodeSetRotation(meshInstanceNode, g_lastMeshInstanceQuatX, g_lastMeshInstanceQuatY, g_lastMeshInstanceQuatZ, g_lastMeshInstanceQuatW);
      g_ymeFuncPtrs.ymePtsdkSceneGraphNodeSetTranslation(meshInstanceNode, g_lastMeshInstancePosX, g_lastMeshInstancePosY, g_lastMeshInstancePosZ);
    }
    igText("");
    igText("Note: M key toggles fullscreen window modes.");
    igInputInt("Window width",  &g_windowWidth,  0, 0, 0);
    igInputInt("Window height", &g_windowHeight, 0, 0, 0);
    if (igButton("Set window dimensions", defaultButtonSize)) {
      glfwSetWindowSize((GLFWwindow *)g_ymeData.ptrTo_m_Window__GLFWwindow, g_windowWidth, g_windowHeight);
    }
    igInputInt("Window FPS limiter", g_ymeData.ptrTo_m_ui_FPSLimiter__int, 0, 0, 0);
    igText("");
    igCheckbox("Enable animations loop", (bool *)&g_enableAnimationsLoop);
    double m_SceneTime = ((double *)g_ymeData.ptrTo_m_SceneTime__double)[0];
    igText("Animations loop time: %f", m_SceneTime);
    igDragFloat("Animations loop time increment", &g_sceneTimeIncrement, g_floatSlidersDragPrecision, 0, 0, 0, 0);
    if (igDragInt("Increment delay frames count", &g_sceneTimeIncrementDelayFramesCount, 1, 0, 0, 0, 0)) {
      if (g_sceneTimeIncrementDelayFramesCount <= 0) {
        g_sceneTimeIncrementDelayFramesCount = 1;
      }
    }
    static float g_customAnimationsTime = 0;
    if (igButton("Increment animations loop time", defaultButtonSize)) {
      double m_SceneTime = ((double *)g_ymeData.ptrTo_m_SceneTime__double)[0];
      m_SceneTime += g_sceneTimeIncrement;
      ((double *)g_ymeData.ptrTo_m_SceneTime__double)[0] = m_SceneTime;

      void * m_Scene = 0;
      g_ymeFuncPtrs.ymePtsdkGetCurrentScenePointer(g_ymeData.ptrTo_m_app__App, &m_Scene);
      uint64_t animationsCount = 0;
      g_ymeFuncPtrs.ymePtsdkGetSceneGraphAnimationsCount(m_Scene, &animationsCount);
      for (uint64_t i = 0; i < animationsCount; i += 1) {
        void * animation = 0;
        g_ymeFuncPtrs.ymePtsdkGetSceneGraphAnimation(m_Scene, i, &animation);
        float animationDuration = 0;
        g_ymeFuncPtrs.ymePtsdkAnimationGetDuration(animation, &animationDuration);
        g_ymeFuncPtrs.ymePtsdkAnimationApply(animation, (float)fmod(m_SceneTime, animationDuration), 0);
      }
      if (g_resetAccumulationOnTimeIncrement == 1) {
        char  * pResetAccumulation = (char *)g_ymeData.ptrTo_m_ui_ResetAccumulation__bool;
        pResetAccumulation[0] = 1;
      }
    }
    if (igDragFloat("Custom animations time", &g_customAnimationsTime, g_floatSlidersDragPrecision, 0, 0, 0, 0)) {
      void * m_Scene = 0;
      g_ymeFuncPtrs.ymePtsdkGetCurrentScenePointer(g_ymeData.ptrTo_m_app__App, &m_Scene);
      uint64_t animationsCount = 0;
      g_ymeFuncPtrs.ymePtsdkGetSceneGraphAnimationsCount(m_Scene, &animationsCount);
      for (uint64_t i = 0; i < animationsCount; i += 1) {
        void * animation = 0;
        g_ymeFuncPtrs.ymePtsdkGetSceneGraphAnimation(m_Scene, i, &animation);
        float animationDuration = 0;
        g_ymeFuncPtrs.ymePtsdkAnimationGetDuration(animation, &animationDuration);
        g_ymeFuncPtrs.ymePtsdkAnimationApply(animation, (float)fmod(g_customAnimationsTime, animationDuration), 0);
      }
      if (g_resetAccumulationOnTimeIncrement == 1) {
        char  * pResetAccumulation = (char *)g_ymeData.ptrTo_m_ui_ResetAccumulation__bool;
        pResetAccumulation[0] = 1;
      }
    }
    if (igButton("Set loop time to custom animations time", defaultButtonSize)) {
      g_customAnimationsTime = ((double *)g_ymeData.ptrTo_m_SceneTime__double)[0];
    }
    if (igButton("Set custom animations time to loop time", defaultButtonSize)) {
      ((double *)g_ymeData.ptrTo_m_SceneTime__double)[0] = g_customAnimationsTime;
    }
    igCheckbox("Reset accumulation on camera move", (bool *)&g_resetAccumulationOnCameraMove);
    igCheckbox("Reset accumulation on animation time increment", (bool *)&g_resetAccumulationOnTimeIncrement);
    igText("Note: X key press resets accumulation as well.");
    igCheckbox("Save screenshot on animation time increment", (bool *)&g_saveScreenshotOnTimeIncrement);
    igText("");
    igInputInt("Instance index", &g_copyXformsToCameraFromInstanceIndex, 0, 0, 0);
    igCheckbox("Copy instance index xforms to camera", (bool *)&g_copyXformsToCameraFromInstance);
  }

  igEnd();
}

static bool IsKeyPressedOnceO() {
  if (((char *)g_ymeData.ptrTo_m_ui_ShowUI__bool)[0] == 1) {
    return false;
  }
  
  static int g_isKeyPressedO     = 0;
  static int g_isKeyPressedOOnce = 0;
  if (glfwGetKey((GLFWwindow *)g_ymeData.ptrTo_m_Window__GLFWwindow, GLFW_KEY_O) == GLFW_PRESS && g_isKeyPressedOOnce == 0) {
    g_isKeyPressedO     = 1;
    g_isKeyPressedOOnce = 1;
  } else {
    g_isKeyPressedO     = 0;
  }
  if (glfwGetKey((GLFWwindow *)g_ymeData.ptrTo_m_Window__GLFWwindow, GLFW_KEY_O) == GLFW_RELEASE) {
    g_isKeyPressedOOnce = 0;
  }
  return g_isKeyPressedO;
}

static bool IsKeyPressedOnceCtrlR() {
  if (glfwGetKey((GLFWwindow *)g_ymeData.ptrTo_m_Window__GLFWwindow, GLFW_KEY_LEFT_CONTROL) != GLFW_PRESS) {
    return false;
  }

  static int g_isKeyPressedR     = 0;
  static int g_isKeyPressedROnce = 0;
  if (glfwGetKey((GLFWwindow *)g_ymeData.ptrTo_m_Window__GLFWwindow, GLFW_KEY_R) == GLFW_PRESS && g_isKeyPressedROnce == 0) {
    g_isKeyPressedR     = 1;
    g_isKeyPressedROnce = 1;
  } else {
    g_isKeyPressedR     = 0;
  }
  if (glfwGetKey((GLFWwindow *)g_ymeData.ptrTo_m_Window__GLFWwindow, GLFW_KEY_R) == GLFW_RELEASE) {
    g_isKeyPressedROnce = 0;
  }
  return g_isKeyPressedR;
}

static bool IsKeyPressedOnceM() {
  if (((char *)g_ymeData.ptrTo_m_ui_ShowUI__bool)[0] == 1) {
    return false;
  }

  static int g_isKeyPressedM     = 0;
  static int g_isKeyPressedMOnce = 0;
  if (glfwGetKey((GLFWwindow *)g_ymeData.ptrTo_m_Window__GLFWwindow, GLFW_KEY_M) == GLFW_PRESS && g_isKeyPressedMOnce == 0) {
    g_isKeyPressedM     = 1;
    g_isKeyPressedMOnce = 1;
  } else {
    g_isKeyPressedM     = 0;
  }
  if (glfwGetKey((GLFWwindow *)g_ymeData.ptrTo_m_Window__GLFWwindow, GLFW_KEY_M) == GLFW_RELEASE) {
    g_isKeyPressedMOnce = 0;
  }
  return g_isKeyPressedM;
}

void ymeAppAnimateCallback(float fElapsedTimeSeconds) {
  if (((int *)g_ymeData.ptrTo_g_ymeSplitScreenPlayersCount__int)[0] > 1) {
    ((int *)g_ymeData.ptrTo_g_ymeSplitScreenCurrentPlayerIndex__int)[0] += 1;
    ((int *)g_ymeData.ptrTo_g_ymeSplitScreenCurrentPlayerIndex__int)[0] %= 2;
  }

  if (0) { // NOTE(Constantine): Gamepad UI controls.
    ImGuiIO * io = igGetIO();
    memset(io->NavInputs, 0, sizeof(io->NavInputs));
    if ((io->ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == ImGuiConfigFlags_NavEnableGamepad) {
      #define MAP_BUTTON(NAV_NO, BUTTON_NO)       { if (buttons_count > BUTTON_NO && buttons[BUTTON_NO] == GLFW_PRESS) io->NavInputs[NAV_NO] = 1.0f; }
      #define MAP_ANALOG(NAV_NO, AXIS_NO, V0, V1) { float v = (axes_count > AXIS_NO) ? axes[AXIS_NO] : V0; v = (v - V0) / (V1 - V0); if (v > 1.0f) v = 1.0f; if (io->NavInputs[NAV_NO] < v) io->NavInputs[NAV_NO] = v; }
      int axes_count     = 0;
      int buttons_count  = 0;
      const float * axes = glfwGetJoystickAxes(0, &axes_count);
      const unsigned char * buttons = glfwGetJoystickButtons(0, &buttons_count);
      MAP_BUTTON(ImGuiNavInput_Activate,    0);  // A / Cross
      MAP_BUTTON(ImGuiNavInput_Cancel,      1);  // B / Circle
      MAP_BUTTON(ImGuiNavInput_Menu,        2);  // X / Square
      MAP_BUTTON(ImGuiNavInput_Input,       3);  // Y / Triangle
      MAP_BUTTON(ImGuiNavInput_DpadLeft,    13); // D-Pad Left
      MAP_BUTTON(ImGuiNavInput_DpadRight,   11); // D-Pad Right
      MAP_BUTTON(ImGuiNavInput_DpadUp,      10); // D-Pad Up
      MAP_BUTTON(ImGuiNavInput_DpadDown,    12); // D-Pad Down
      MAP_BUTTON(ImGuiNavInput_FocusPrev,   4);  // LB / L1
      MAP_BUTTON(ImGuiNavInput_FocusNext,   5);  // RB / R1
      MAP_BUTTON(ImGuiNavInput_TweakSlow,   4);  // LB / L1
      MAP_BUTTON(ImGuiNavInput_TweakFast,   5);  // RB / R1
      MAP_ANALOG(ImGuiNavInput_LStickLeft,  0, -0.3f, -0.9f);
      MAP_ANALOG(ImGuiNavInput_LStickRight, 0, +0.3f, +0.9f);
      MAP_ANALOG(ImGuiNavInput_LStickUp,    1, -0.3f, -0.9f);
      MAP_ANALOG(ImGuiNavInput_LStickDown,  1, +0.3f, +0.9f);
      #undef MAP_BUTTON
      #undef MAP_ANALOG
      if (axes_count > 0 && buttons_count > 0) {
        io->BackendFlags |= ImGuiBackendFlags_HasGamepad;
      } else {
        io->BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
      }
    }
  }

  if (glfwGetKey((GLFWwindow *)g_ymeData.ptrTo_m_Window__GLFWwindow, GLFW_KEY_X) && (((char *)g_ymeData.ptrTo_m_ui_ShowUI__bool)[0] == 0)) {
    char  * pResetAccumulation = (char *)g_ymeData.ptrTo_m_ui_ResetAccumulation__bool;
    pResetAccumulation[0] = 1;
  }

  static int g_windowPosX = 0;
  static int g_windowPosY = 0;
  static int g_fullscreen = 0;
  if (IsKeyPressedOnceM()) {
    g_fullscreen = !g_fullscreen;
    if (g_fullscreen == 1) {
      glfwGetWindowPos((GLFWwindow *)g_ymeData.ptrTo_m_Window__GLFWwindow, &g_windowPosX, &g_windowPosY);
      const GLFWvidmode * mode = glfwGetVideoMode(glfwGetPrimaryMonitor());
      glfwSetWindowMonitor((GLFWwindow *)g_ymeData.ptrTo_m_Window__GLFWwindow, glfwGetPrimaryMonitor(), 0, 0, mode->width, mode->height, g_monitorRefreshRate);
      glfwSetWindowSize((GLFWwindow *)g_ymeData.ptrTo_m_Window__GLFWwindow, g_windowWidth, g_windowHeight);
    } else {
      glfwSetWindowMonitor((GLFWwindow *)g_ymeData.ptrTo_m_Window__GLFWwindow, 0, g_windowPosX, g_windowPosY, g_windowWidth, g_windowHeight, g_monitorRefreshRate);
    }
  }

  GLFWgamepadstate gamepadState = {0};
  if (0) {
    glfwGetGamepadState(0, &gamepadState);
  } else {
    if (igIsMouseClicked(ImGuiMouseButton_Middle, 0) || IsKeyPressedOnceCtrlR()) {
      g_cameraMode = !g_cameraMode;
      if (g_cameraMode == 0) {
        char * pShowUi = (char *)g_ymeData.ptrTo_m_ui_ShowUI__bool;
        pShowUi[0]     = 1;
        glfwSetInputMode((GLFWwindow *)g_ymeData.ptrTo_m_Window__GLFWwindow, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
      } else {
        char * pShowUi = (char *)g_ymeData.ptrTo_m_ui_ShowUI__bool;
        pShowUi[0]     = 0;
        glfwSetInputMode((GLFWwindow *)g_ymeData.ptrTo_m_Window__GLFWwindow, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
        double mouseX = 0;
        double mouseY = 0;
        glfwGetCursorPos((GLFWwindow *)g_ymeData.ptrTo_m_Window__GLFWwindow, &mouseX, &mouseY);
        g_prevMouseX = mouseX;
        g_prevMouseY = mouseY;
      }
    }
    if (g_cameraMode == 0) {
      gamepadState.buttons[GLFW_GAMEPAD_BUTTON_B] = igIsMouseDown(ImGuiMouseButton_Right);
    }
    if (g_cameraMode == 1) {
      double mouseX = 0;
      double mouseY = 0;
      glfwGetCursorPos((GLFWwindow *)g_ymeData.ptrTo_m_Window__GLFWwindow, &mouseX, &mouseY);
      double diffMouseX = mouseX - g_prevMouseX;
      double diffMouseY = mouseY - g_prevMouseY;
      g_prevMouseX = mouseX;
      g_prevMouseY = mouseY;

      static double diffMouseSmoothX = 0;
      static double diffMouseSmoothY = 0;
      diffMouseSmoothX *= 0.75;
      diffMouseSmoothY *= 0.75;
      diffMouseSmoothX += diffMouseX;
      diffMouseSmoothY += diffMouseY;

      int keyPressW = glfwGetKey((GLFWwindow *)g_ymeData.ptrTo_m_Window__GLFWwindow, GLFW_KEY_W);
      int keyPressA = glfwGetKey((GLFWwindow *)g_ymeData.ptrTo_m_Window__GLFWwindow, GLFW_KEY_A);
      int keyPressS = glfwGetKey((GLFWwindow *)g_ymeData.ptrTo_m_Window__GLFWwindow, GLFW_KEY_S);
      int keyPressD = glfwGetKey((GLFWwindow *)g_ymeData.ptrTo_m_Window__GLFWwindow, GLFW_KEY_D);
      int keyPressE = glfwGetKey((GLFWwindow *)g_ymeData.ptrTo_m_Window__GLFWwindow, GLFW_KEY_E);
      int keyPressQ = glfwGetKey((GLFWwindow *)g_ymeData.ptrTo_m_Window__GLFWwindow, GLFW_KEY_Q);

      gamepadState.buttons[GLFW_GAMEPAD_BUTTON_A] = 0;
      gamepadState.buttons[GLFW_GAMEPAD_BUTTON_B] = 0;
      gamepadState.buttons[GLFW_GAMEPAD_BUTTON_X] = 0;
      gamepadState.buttons[GLFW_GAMEPAD_BUTTON_Y] = 0;
      gamepadState.buttons[GLFW_GAMEPAD_BUTTON_LEFT_BUMPER] = 0;
      gamepadState.buttons[GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER] = 0;
      gamepadState.buttons[GLFW_GAMEPAD_BUTTON_BACK] = 0;
      gamepadState.buttons[GLFW_GAMEPAD_BUTTON_START] = 0;
      gamepadState.buttons[GLFW_GAMEPAD_BUTTON_GUIDE] = 0;
      gamepadState.buttons[GLFW_GAMEPAD_BUTTON_LEFT_THUMB] = 0;
      gamepadState.buttons[GLFW_GAMEPAD_BUTTON_RIGHT_THUMB] = 0;
      gamepadState.buttons[GLFW_GAMEPAD_BUTTON_DPAD_UP] = 0;
      gamepadState.buttons[GLFW_GAMEPAD_BUTTON_DPAD_RIGHT] = 0;
      gamepadState.buttons[GLFW_GAMEPAD_BUTTON_DPAD_DOWN] = 0;
      gamepadState.buttons[GLFW_GAMEPAD_BUTTON_DPAD_LEFT] = 0;

      gamepadState.axes[GLFW_GAMEPAD_AXIS_LEFT_X] = (keyPressA * -1) + keyPressD;
      gamepadState.axes[GLFW_GAMEPAD_AXIS_LEFT_Y] = (keyPressW * -1) + keyPressS;
      gamepadState.axes[GLFW_GAMEPAD_AXIS_RIGHT_X] = diffMouseSmoothX * g_mouseSensitivity;
      gamepadState.axes[GLFW_GAMEPAD_AXIS_RIGHT_Y] = diffMouseSmoothY * g_mouseSensitivity;
      gamepadState.axes[GLFW_GAMEPAD_AXIS_LEFT_TRIGGER] = keyPressQ;
      gamepadState.axes[GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER] = keyPressE;
    }
  }

  GLFWgamepadstate gamepad2State = {0};
  //glfwGetGamepadState(1, &gamepad2State);

  if (0) { // NOTE(Constantine): Activate split screen at runtime.
    int * currentPlayersCount = (int *)g_ymeData.ptrTo_g_ymeSplitScreenPlayersCount__int;
    if (currentPlayersCount[0] > 1) {
      currentPlayersCount[0] = 1;
    } else {
      currentPlayersCount[0] = 2;
    }
    if (currentPlayersCount[0] > 1) {
      ((char *)g_ymeData.ptrTo_m_ui_RealtimeDenoiser__bool)[0]                         = 1;
      ((int *)g_ymeData.ptrTo_m_ui_RealtimeAA__int)[0]                                 = 0;
      ((char *)g_ymeData.ptrTo_m_ui_RealtimeNoise__bool)[0]                            = 0;
      ((char *)g_ymeData.ptrTo_m_ui_UseReSTIR__bool)[0]                                = 0;
      ((char *)g_ymeData.ptrTo_m_ui_UseReSTIRGI__bool)[0]                              = 0;
      ((unsigned *)g_ymeData.ptrTo_m_ui_RelaxSettings_historyFixFrameNum__unsigned)[0] = 60;
      ((float *)g_ymeData.ptrTo_m_ui_RelaxSettings_specularPhiLuminance__float)[0]     = 10;
    } else {
      ((char *)g_ymeData.ptrTo_m_ui_RealtimeDenoiser__bool)[0]                         = 1;
      ((int *)g_ymeData.ptrTo_m_ui_RealtimeAA__int)[0]                                 = 2; // "DLSS"
      ((char *)g_ymeData.ptrTo_m_ui_RealtimeNoise__bool)[0]                            = 1;
      ((char *)g_ymeData.ptrTo_m_ui_UseReSTIR__bool)[0]                                = 0;
      ((char *)g_ymeData.ptrTo_m_ui_UseReSTIRGI__bool)[0]                              = 0;
      ((unsigned *)g_ymeData.ptrTo_m_ui_RelaxSettings_historyFixFrameNum__unsigned)[0] = 4;
      ((float *)g_ymeData.ptrTo_m_ui_RelaxSettings_specularPhiLuminance__float)[0]     = 0.350;
    }
    ((int *)g_ymeData.ptrTo_g_ymeSplitScreenCurrentPlayerIndex__int)[0] = 0;
  }

  //if (gamepadState.buttons[1] == 1) {
  //  g_ymeFuncPtrs.ymePtsdkSetCameraVerticalFOVRad(g_ymeData.ptrTo_m_app__App, 0.27266463);
  //}

  //if (gamepadState.buttons[1] == 1) {
  //  char * ptrTo_m_ui_UseReSTIR   = (char *)g_ymeData.ptrTo_m_ui_UseReSTIR__bool;
  //  char * ptrTo_m_ui_UseReSTIRGI = (char *)g_ymeData.ptrTo_m_ui_UseReSTIRGI__bool;
  //  ptrTo_m_ui_UseReSTIR[0]   = 1;
  //  ptrTo_m_ui_UseReSTIRGI[0] = 1;
  //}

  //if (gamepadState.buttons[1] == 1) {
  //  char *     m_ui_EnableToneMapping                 = (char *)g_ymeData.ptrTo_m_ui_EnableToneMapping__bool;
  //  unsigned * m_ui_ToneMappingParams_toneMapOperator = (char *)g_ymeData.ptrTo_m_ui_ToneMappingParams_toneMapOperator__unsigned;
  //  m_ui_EnableToneMapping[0]                 = 1;
  //  m_ui_ToneMappingParams_toneMapOperator[0] = 5; // ACES
  //}

  //if (gamepadState.buttons[1] == 1) {
  //  char * ptrTo_m_ui_ExperimentalPhotoModeScreenshot = (char *)g_ymeData.ptrTo_m_ui_ExperimentalPhotoModeScreenshot__bool;
  //  ptrTo_m_ui_ExperimentalPhotoModeScreenshot[0] = 1;
  //}

  //if (gamepadState.buttons[1] == 1) {
  //  g_ymeFuncPtrs.ymePtsdkSetCurrentScene(g_ymeData.ptrTo_m_app__App, "programmer-art.scene.json", 1);
  //}
  //if (gamepadState.buttons[2] == 1) {
  //  g_ymeFuncPtrs.ymePtsdkSetCurrentScene(g_ymeData.ptrTo_m_app__App, "programmer-art-2.scene.json", 0);
  //}

  if (IsKeyPressedOnceO() && FileExists(g_sceneJsonFilepathToLoad)) {
    g_ymeFuncPtrs.ymePtsdkSetCurrentSceneName(g_ymeData.ptrTo_m_app__App, g_sceneJsonFilepathToLoad);
    g_ymeFuncPtrs.ymePtsdkAppBaseSetAsynchronousLoadingEnabled(g_ymeData.ptrTo_m_app__App, 0);
    g_ymeFuncPtrs.ymePtsdkAppBaseBeginLoadingScene(g_ymeData.ptrTo_m_app__App, g_sceneJsonFilepathToLoad);
  }

  //if (gamepadState.buttons[1] == 1) {
  //  glfwSetWindowTitle((GLFWwindow *)g_ymeData.ptrTo_m_Window__GLFWwindow, "Button pressed!");
  //}

  //if (gamepadState.buttons[1] == 1) {
  //  // NOTE(Constantine): Needs ymeAppSceneLoadedOverrideInvisibleLightsCleanupCallback::doCleanup set to 0.
  //  void * m_Scene = 0;
  //  g_ymeFuncPtrs.ymePtsdkGetCurrentScenePointer(g_ymeData.ptrTo_m_app__App, &m_Scene);
  //  void * light = 0;
  //  g_ymeFuncPtrs.ymePtsdkGetSceneGraphLight(m_Scene, 0, &light);
  //  const char * lightName = 0; // "DirectionalLight"
  //  g_ymeFuncPtrs.ymePtsdkSceneGraphLeafGetName(light, &lightName);
  //  YmeLightType lightType = YME_LIGHT_TYPE_NONE; // YME_LIGHT_TYPE_DIRECTIONAL
  //  g_ymeFuncPtrs.ymePtsdkLightGetType(light, &lightType);
  //  g_ymeFuncPtrs.ymePtsdkSceneGraphLeafSetProperty(light, "color", 0, 0, 1, 1);
  //  g_ymeFuncPtrs.ymePtsdkSceneGraphLeafSetProperty(light, "irradiance", 100, 0, 0, 0);
  //}

  g_sceneTimeIncrementDelayCounter += 1;
  if (g_enableAnimationsLoop == 1 && (g_sceneTimeIncrementDelayCounter % g_sceneTimeIncrementDelayFramesCount == (g_sceneTimeIncrementDelayFramesCount-1))) {
    if (g_saveScreenshotOnTimeIncrement == 1) {
      char * ptrTo_m_ui_ExperimentalPhotoModeScreenshot = (char *)g_ymeData.ptrTo_m_ui_ExperimentalPhotoModeScreenshot__bool;
      ptrTo_m_ui_ExperimentalPhotoModeScreenshot[0] = 1;
    }
  }
  if (g_enableAnimationsLoop == 1 && (g_sceneTimeIncrementDelayCounter % g_sceneTimeIncrementDelayFramesCount == 0)) {
    double m_SceneTime = ((double *)g_ymeData.ptrTo_m_SceneTime__double)[0];
    m_SceneTime += g_sceneTimeIncrement;
    ((double *)g_ymeData.ptrTo_m_SceneTime__double)[0] = m_SceneTime;

    void * m_Scene = 0;
    g_ymeFuncPtrs.ymePtsdkGetCurrentScenePointer(g_ymeData.ptrTo_m_app__App, &m_Scene);
    uint64_t animationsCount = 0;
    g_ymeFuncPtrs.ymePtsdkGetSceneGraphAnimationsCount(m_Scene, &animationsCount);
    for (uint64_t i = 0; i < animationsCount; i += 1) {
      void * animation = 0;
      g_ymeFuncPtrs.ymePtsdkGetSceneGraphAnimation(m_Scene, i, &animation);
      float animationDuration = 0;
      g_ymeFuncPtrs.ymePtsdkAnimationGetDuration(animation, &animationDuration);
      g_ymeFuncPtrs.ymePtsdkAnimationApply(animation, (float)fmod(m_SceneTime, animationDuration), 0);
    }
    if (g_resetAccumulationOnTimeIncrement == 1) {
      char  * pResetAccumulation = (char *)g_ymeData.ptrTo_m_ui_ResetAccumulation__bool;
      pResetAccumulation[0] = 1;
    }
  }

  if (0) { // NOTE(Constantine): Changing material at runtime example.
    struct YmeMaterialStructDataSubset {
        float baseOrDiffuseColor[3]; // metal-rough: base color, spec-gloss: diffuse color (if no texture present)
        float specularColor[3]; // spec-gloss: specular color
        float emissiveColor[3];
        float emissiveIntensity; // additional multiplier for emissiveColor
        float metalness; // metal-rough only
        float roughness; // both metal-rough and spec-gloss
        float opacity; // for transparent materials; multiplied by diffuse.a if present
        float alphaCutoff; // for alpha tested materials
        float transmissionFactor; // see KHR_materials_transmission; undefined on specular-gloss materials
        float diffuseTransmissionFactor; // like specularTransmissionFactor, except using diffuse transmission lobe (roughness ignored)
        float normalTextureScale;
        float occlusionStrength;
        float ior; // index of refraction, see KHR_materials_ior

        // Toggle between two PBR models: metal-rough and specular-gloss.
        // See the comments on the other fields here.
        bool useSpecularGlossModel;

        // Toggles for the textures. Only effective if the corresponding texture is non-null.
        bool enableBaseOrDiffuseTexture;
        bool enableMetalRoughOrSpecularTexture;
        bool enableNormalTexture;
        bool enableEmissiveTexture;
        bool enableOcclusionTexture;
        bool enableTransmissionTexture;

        bool doubleSided;

        bool thinSurface;   // As per Falcor/pt_sdk convention, ray hitting a material with the thin surface is assumed to enter and leave surface in the same bounce and it makes most sense when used with doubleSided; it skips all volume logic.

        bool excludeFromNEE; // The mesh will not be part of NEE.

        bool psdExclude; // will not propagate dominant stable plane when doing path space decomposition
        int psdDominantDeltaLobe; // for path space decomposition: -1 means no dominant; 0 usually means transmission, 1 usually means reflection, 2 usually means clearcoat reflection - must match corresponding BSDFSample::getDeltaLobeIndex()!
        
        int nestedPriority; // kMaterialMaxNestedPriority;     // When volume meshes overlap, will cause higher nestedPriority mesh to 'carve out' the volumes with lower nestedPriority (see https://www.sidefx.com/docs/houdini/render/nested.html)

        // KHR_materials_volume - see https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_materials_volume#properties
        float volumeThicknessFactor; // in path tracing this is only used to set "thinSurface == thicknessFactor != 0" with value otherwise ignored (since we get exact thickness)
        float volumeAttenuationDistance;
        float volumeAttenuationColor[3];

        int materialID;
        bool dirty; // set this to true to make Scene update the material data
    };

    void * m_Scene = 0;
    g_ymeFuncPtrs.ymePtsdkGetCurrentScenePointer(g_ymeData.ptrTo_m_app__App, &m_Scene);
    void * material = 0;
    g_ymeFuncPtrs.ymePtsdkGetSceneGraphMaterialByName(m_Scene, "Material-1-Cube", &material);
    struct YmeMaterialStructDataSubset * materialData = 0;
    g_ymeFuncPtrs.ymePtsdkMaterialGetStructData(material, 0, 0, (void **)&materialData);
    materialData->volumeAttenuationColor[0] = 0;
    materialData->volumeAttenuationColor[1] = 1;
    materialData->volumeAttenuationColor[2] = 0;
    materialData->dirty = true;

    // NOTE(Constantine): Some of these are probably not neccessary for this particular material attenuation color change.
    void * rootNode = 0;
    g_ymeFuncPtrs.ymePtsdkGetSceneGraphRootNode(m_Scene, &rootNode);
    g_ymeFuncPtrs.ymePtsdkSceneGraphNodeInvalidateContent(rootNode);
    ((char *)g_ymeData.ptrTo_m_ui_AS_IsDirty__bool)[0] = 1;
    ((float *)g_ymeData.ptrTo_m_ui_ShaderReloadDelayedRequest__float)[0] = 1.0f;
  }

  int currentPlayerIndex = ((int *)g_ymeData.ptrTo_g_ymeSplitScreenCurrentPlayerIndex__int)[0];

  if (1) {
    // NOTE(Constantine): Kinda junky since both can't move objects at the same time, but as an example it's fine.
    if (currentPlayerIndex == 0) { // NOTE(Constantine): Querying geometry intersection at runtime example.
      static int g_pick = 0; // NOTE(Constantine): The results of a pick query are available only on the second frame.
      if (gamepadState.buttons[1] == 1 || g_pick > 0) {
        if (g_pick == 0) {
          g_pick = 1;
          ((char *)g_ymeData.ptrTo_m_PickFindMaterial__bool)[0]           = 1;
          ((char *)g_ymeData.ptrTo_m_Pick__bool)[0]                       = 1;
          ((char *)g_ymeData.ptrTo_m_ui_ContinuousDebugFeedback__bool)[0] = 0;
          ((char *)g_ymeData.ptrTo_m_ui_ShowDebugLines__bool)[0]          = 0;
          unsigned w = 0;
          unsigned h = 0;
          g_ymeFuncPtrs.ymePtsdkAppGetResolutionInfo(g_ymeData.ptrTo_m_app__App, &w, &h, 0, 0);
          if (0) { // NOTE(Constantine): Pick from the center of the screen for gamepads, otherwise from the current mouse position.
            ((unsigned *)g_ymeData.ptrTo_m_ui_DebugPixel_x__uint2)[0] = w / 2;
            ((unsigned *)g_ymeData.ptrTo_m_ui_DebugPixel_x__uint2)[1] = h / 2;
          } else {
            double mouseX = 0;
            double mouseY = 0;
            glfwGetCursorPos((GLFWwindow *)g_ymeData.ptrTo_m_Window__GLFWwindow, &mouseX, &mouseY);
            int windowWidth  = 0;
            int windowHeight = 0;
            glfwGetWindowSize((GLFWwindow *)g_ymeData.ptrTo_m_Window__GLFWwindow, &windowWidth, &windowHeight);
            ((unsigned *)g_ymeData.ptrTo_m_ui_DebugPixel_x__uint2)[0] = (mouseX / (float)windowWidth)  * w;
            ((unsigned *)g_ymeData.ptrTo_m_ui_DebugPixel_x__uint2)[1] = (mouseY / (float)windowHeight) * h;
          }
        } else {
          g_pick = 0;
          int materialID    = ((int *)g_ymeData.ptrTo_m_ui_SelectedMaterialID__int)[0];
          int instanceIndex = ((int *)g_ymeData.ptrTo_m_ui_SelectedInstanceIndex__int)[0];
          int geometryIndex = ((int *)g_ymeData.ptrTo_m_ui_SelectedGeometryIndex__int)[0];
          void * m_Scene = 0;
          g_ymeFuncPtrs.ymePtsdkGetCurrentScenePointer(g_ymeData.ptrTo_m_app__App, &m_Scene);
          void * material = 0;
          g_ymeFuncPtrs.ymePtsdkGetSceneGraphMaterialByID(m_Scene, materialID, &material);
          if (material != 0) {
            g_ymeFuncPtrs.ymePtsdkMaterialGetStructData(material, &g_lastPickMaterialName, 0, 0);
          } else {
            g_lastPickMaterialName = 0;
          }
          if (materialID != 0) {
            void * meshInstance = 0;
            g_ymeFuncPtrs.ymePtsdkGetSceneGraphMeshInstance(m_Scene, instanceIndex, &meshInstance);
            // Get mesh name
            void * mesh = 0;
            g_ymeFuncPtrs.ymePtsdkMeshInstanceGetMesh(meshInstance, &mesh);
            g_ymeFuncPtrs.ymePtsdkMeshGetStructData(mesh, &g_lastPickMeshName, 0, 0);
            // Get mesh instance name
            g_ymeFuncPtrs.ymePtsdkSceneGraphLeafGetName(meshInstance, &g_lastPickMeshInstanceName);
          }
          if (0) { // Move selected object
            if (materialID != 0) {
              void * meshInstance = 0;
              g_ymeFuncPtrs.ymePtsdkGetSceneGraphMeshInstance(m_Scene, instanceIndex, &meshInstance);
              void * meshInstanceNode = 0;
              g_ymeFuncPtrs.ymePtsdkSceneGraphLeafGetNode(meshInstance, &meshInstanceNode);
              double px = 0;
              double py = 0;
              double pz = 0;
              g_ymeFuncPtrs.ymePtsdkSceneGraphNodeGetTranslation(meshInstanceNode, &px, &py, &pz);
              g_ymeFuncPtrs.ymePtsdkSceneGraphNodeSetTranslation(meshInstanceNode, px, py + 0.075f, pz);
            }
          }
        }
      }
    }
    // NOTE(Constantine): Complete copy of the above for player 2:
    if (currentPlayerIndex == 1) { // NOTE(Constantine): Querying geometry intersection at runtime example.
      static int g_pick = 0; // NOTE(Constantine): The results of a pick query are available only on the second frame.
      if (gamepad2State.buttons[1] == 1 || g_pick > 0) {
        if (g_pick == 0) {
          g_pick = 1;
          ((char *)g_ymeData.ptrTo_m_PickFindMaterial__bool)[0]           = 1;
          ((char *)g_ymeData.ptrTo_m_Pick__bool)[0]                       = 1;
          ((char *)g_ymeData.ptrTo_m_ui_ContinuousDebugFeedback__bool)[0] = 0;
          ((char *)g_ymeData.ptrTo_m_ui_ShowDebugLines__bool)[0]          = 0;
          unsigned w = 0;
          unsigned h = 0;
          g_ymeFuncPtrs.ymePtsdkAppGetResolutionInfo(g_ymeData.ptrTo_m_app__App, &w, &h, 0, 0);
          ((unsigned *)g_ymeData.ptrTo_m_ui_DebugPixel_x__uint2)[0] = w / 2;
          ((unsigned *)g_ymeData.ptrTo_m_ui_DebugPixel_x__uint2)[1] = h / 2;
        } else {
          g_pick = 0;
          int materialID    = ((int *)g_ymeData.ptrTo_m_ui_SelectedMaterialID__int)[0];
          int instanceIndex = ((int *)g_ymeData.ptrTo_m_ui_SelectedInstanceIndex__int)[0];
          int geometryIndex = ((int *)g_ymeData.ptrTo_m_ui_SelectedGeometryIndex__int)[0];
          void * m_Scene = 0;
          g_ymeFuncPtrs.ymePtsdkGetCurrentScenePointer(g_ymeData.ptrTo_m_app__App, &m_Scene);
          void * material = 0;
          g_ymeFuncPtrs.ymePtsdkGetSceneGraphMaterialByID(m_Scene, materialID, &material);
          if (material != 0) {
            g_ymeFuncPtrs.ymePtsdkMaterialGetStructData(material, &g_lastPickMaterialName, 0, 0);
          } else {
            g_lastPickMaterialName = 0;
          }
          // Move selected object
          if (materialID != 0) {
            void * meshInstance = 0;
            g_ymeFuncPtrs.ymePtsdkGetSceneGraphMeshInstance(m_Scene, instanceIndex, &meshInstance);
            void * meshInstanceNode = 0;
            g_ymeFuncPtrs.ymePtsdkSceneGraphLeafGetNode(meshInstance, &meshInstanceNode);
            double px = 0;
            double py = 0;
            double pz = 0;
            g_ymeFuncPtrs.ymePtsdkSceneGraphNodeGetTranslation(meshInstanceNode, &px, &py, &pz);
            g_ymeFuncPtrs.ymePtsdkSceneGraphNodeSetTranslation(meshInstanceNode, px, py + 0.075f, pz);
            // Get mesh name
            void * mesh = 0;
            g_ymeFuncPtrs.ymePtsdkMeshInstanceGetMesh(meshInstance, &mesh);
            g_ymeFuncPtrs.ymePtsdkMeshGetStructData(mesh, &g_lastPickMeshName, 0, 0);
            // Get mesh instance name
            g_ymeFuncPtrs.ymePtsdkSceneGraphLeafGetName(meshInstance, &g_lastPickMeshInstanceName);
          }
        }
      }
    }
  }

  static float g_cam2PosX     = 0.f;
  static float g_cam2PosY     = 0.f;
  static float g_cam2PosZ     = 0.f;
  static float g_cam2PitchDeg = 0.f;
  static float g_cam2YawDeg   = 0.f;

  if (g_copyXformsToCameraFromInstance == 1) {
    void * m_Scene = 0;
    g_ymeFuncPtrs.ymePtsdkGetCurrentScenePointer(g_ymeData.ptrTo_m_app__App, &m_Scene);
    void * meshInstance = 0;
    g_ymeFuncPtrs.ymePtsdkGetSceneGraphMeshInstance(m_Scene, g_copyXformsToCameraFromInstanceIndex, &meshInstance);
    void * meshInstanceNode = 0;
    g_ymeFuncPtrs.ymePtsdkSceneGraphLeafGetNode(meshInstance, &meshInstanceNode);

    double px = 0;
    double py = 0;
    double pz = 0;
    g_ymeFuncPtrs.ymePtsdkSceneGraphNodeGetTranslation(meshInstanceNode, &px, &py, &pz);
    double qx = 0;
    double qy = 0;
    double qz = 0;
    double qw = 0;
    g_ymeFuncPtrs.ymePtsdkSceneGraphNodeGetRotation(meshInstanceNode, &qx, &qy, &qz, &qw);

    float targetRotation_row0_x = 0.f;
    float targetRotation_row0_y = 0.f;
    float targetRotation_row0_z = 0.f;
    float targetRotation_row1_x = 0.f;
    float targetRotation_row1_y = 0.f;
    float targetRotation_row1_z = 0.f;
    float targetRotation_row2_x = 0.f;
    float targetRotation_row2_y = 0.f;
    float targetRotation_row2_z = 0.f;
    g_ymeFuncPtrs.ymePtsdkDonutMathQuatToFloat3x3(qx, qy, qz, qw,
      &targetRotation_row0_x,
      &targetRotation_row0_y,
      &targetRotation_row0_z,
      &targetRotation_row1_x,
      &targetRotation_row1_y,
      &targetRotation_row1_z,
      &targetRotation_row2_x,
      &targetRotation_row2_y,
      &targetRotation_row2_z
    );

    float cameraRightX = -targetRotation_row0_x;
    float cameraRightY = -targetRotation_row0_y;
    float cameraRightZ = -targetRotation_row0_z;

    float cameraUpX    =  targetRotation_row1_x;
    float cameraUpY    =  targetRotation_row1_y;
    float cameraUpZ    =  targetRotation_row1_z;

    float cameraDirX   =  targetRotation_row2_x;
    float cameraDirY   =  targetRotation_row2_y;
    float cameraDirZ   =  targetRotation_row2_z;

    g_ymeFuncPtrs.ymePtsdkCameraUpdate(g_ymeData.ptrTo_m_Camera__donut_app_FirstPersonCamera,
      px, py, pz,
      cameraDirX, cameraDirY, cameraDirZ,
      cameraUpX, cameraUpY, cameraUpZ,
      cameraRightX, cameraRightY, cameraRightZ
    );
  }

  if (g_copyXformsToCameraFromInstance == 0) {
    if (currentPlayerIndex == 0) {
      CameraUpdate(gamepadState, &g_cam1PosX, &g_cam1PosY, &g_cam1PosZ, &g_cam1PitchDeg, &g_cam1YawDeg);
    } else {
      CameraUpdate(gamepad2State, &g_cam2PosX, &g_cam2PosY, &g_cam2PosZ, &g_cam2PitchDeg, &g_cam2YawDeg);
    }
  }

  if (0) {
    void * m_Scene = 0;
    g_ymeFuncPtrs.ymePtsdkGetCurrentScenePointer(g_ymeData.ptrTo_m_app__App, &m_Scene);
    uint64_t lightsCount = 0;
    uint64_t materialsCount = 0;
    uint64_t meshesCount = 0;
    uint64_t meshInstancesCount = 0;
    uint64_t skinnedMeshInstancesCount = 0;
    g_ymeFuncPtrs.ymePtsdkGetSceneGraphLightsCount(m_Scene, &lightsCount);
    g_ymeFuncPtrs.ymePtsdkGetSceneGraphMaterialsCount(m_Scene, &materialsCount);
    g_ymeFuncPtrs.ymePtsdkGetSceneGraphMeshesCount(m_Scene, &meshesCount);
    g_ymeFuncPtrs.ymePtsdkGetSceneGraphMeshInstancesCount(m_Scene, &meshInstancesCount);
    g_ymeFuncPtrs.ymePtsdkGetSceneGraphSkinnedMeshInstancesCount(m_Scene, &skinnedMeshInstancesCount);
    volatile int x = 0;
  }

  if (0) {
    void * m_Scene = 0;
    g_ymeFuncPtrs.ymePtsdkGetCurrentScenePointer(g_ymeData.ptrTo_m_app__App, &m_Scene);
    void * meshInstance = 0;
    g_ymeFuncPtrs.ymePtsdkGetSceneGraphMeshInstance(m_Scene, 0, &meshInstance);
    void * meshInstanceNode = 0;
    g_ymeFuncPtrs.ymePtsdkSceneGraphLeafGetNode(meshInstance, &meshInstanceNode);
    static float y = 0.f;
    y += 0.005f;
    g_ymeFuncPtrs.ymePtsdkSceneGraphNodeSetTranslation(meshInstanceNode, 0, y, 0);
  }

  if (0) {
    void * m_Scene = 0;
    g_ymeFuncPtrs.ymePtsdkGetCurrentScenePointer(g_ymeData.ptrTo_m_app__App, &m_Scene);
    void * meshInstance = 0;
    g_ymeFuncPtrs.ymePtsdkGetSceneGraphMeshInstance(m_Scene, 330, &meshInstance);
    void * meshInstanceNode = 0;
    g_ymeFuncPtrs.ymePtsdkSceneGraphLeafGetNode(meshInstance, &meshInstanceNode);

    //g_ymeFuncPtrs.ymePtsdkSceneGraphNodeSetRotation(meshInstanceNode, quat[0], quat[1], quat[2], quat[3]);
    //g_ymeFuncPtrs.ymePtsdkSceneGraphNodeSetTranslation(meshInstanceNode, pos[0], pos[1], pos[2]);
  }
}
